package com.free.bsf.metric.base;

import com.free.bsf.core.base.Callable;
import com.free.bsf.core.util.LogUtils;
import com.free.bsf.core.util.ThreadUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.val;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * 无锁缓存,允许失效时并发执行
 */
public class Cache {
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    private class CacheInfo{
        private long cacheTime=0L;

        private int expire=0;
        private Object data=null;
    }
    private ConcurrentHashMap<String,CacheInfo> cacheMap = new ConcurrentHashMap<>();

    public Cache(){
        ThreadUtils.system().submit("清理缓存任务",()->{
            while (!ThreadUtils.system().isShutdown()) {
                try {
                    val delKeys = new ArrayList<>();
                    for (val kv : cacheMap.entrySet()) {
                        val value = kv.getValue();
                        if (value == null) {
                            delKeys.add(kv.getKey());
                            continue;
                        }
                        if ((System.currentTimeMillis() - value.cacheTime) > 2*value.expire) {
                            delKeys.add(kv.getKey());
                        }
                    }
                    for (val del : delKeys) {
                        cacheMap.remove(del);
                    }
                    if(cacheMap.size()>1000){
                        LogUtils.error(this.getClass(),"","当前缓存过大,size为"+cacheMap.size()+",执行强制清理....");
                        cacheMap.clear();
                    }
                } catch (Exception e) {
                    LogUtils.error(this.getClass(), "", "清理缓存出错", e);
                }
                ThreadUtils.sleep(5000);
            }
        });
    }

    public  <T> T get(int expire,String key, Callable.Func0<T> func){
        val cacheInfo = cacheMap.get(key);
        if(cacheInfo==null||(System.currentTimeMillis()-cacheInfo.cacheTime)>expire){
            val d = func.invoke();
            cacheMap.put(key,new CacheInfo(System.currentTimeMillis(),expire,d));
            return d;
        }else{
            return (T)cacheInfo.data;
        }
    }
}
